package com.spz.demo.singleboot.shiro.config;

import com.spz.demo.singleboot.shiro.filter.ShiroLoginFilter;
import com.spz.demo.singleboot.shiro.matcher.PasswordCredentialsMatcher;
import com.spz.demo.singleboot.shiro.realm.UserRealm;
import com.spz.demo.singleboot.shiro.token.UserAuthenticationToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSubjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.*;

/**
 * Shiro 配置
 * 禁用 Shiro Session 步骤：
 *      1. SubjectContext 在创建的时候，需要关闭 session 的创建，这个由 DefaultWebSubjectFactory.createSubject 管理。
 *          参考自定义类：ASubjectFactory.java
 *      2. 禁用使用 Sessions 作为存储策略的实现，这个由 securityManager 的 subjectDao.sessionStorageEvaluator 管理
 *      3. 禁用掉会话调度器，这个由 sessionManager 管理
 */
@Slf4j//(topic = "SYS_LOG")
@Configuration
public class ShiroConfig {

    @Autowired
    private UserRealm userRealm;

    /**
     * Shiro 安全管理器
     *
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();

        // 设置自定义的 SubjectFactory
        manager.setSubjectFactory(subjectFactory());

        // 设置自定义的 SessionManager
        manager.setSessionManager(sessionManager());

        // 禁用 Session
        ((DefaultSessionStorageEvaluator)((DefaultSubjectDAO)manager.getSubjectDAO()).getSessionStorageEvaluator())
                .setSessionStorageEnabled(false);

        // 设置自定义的 Realm
        manager.setRealms(getRealms());

        return manager;
    }

    /**
     * 设置过滤规则
     *
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //自定义拦截器
        Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
        filtersMap.put("shiroLoginFilter", new ShiroLoginFilter());//登录验证拦截器
        shiroFilterFactoryBean.setFilters(filtersMap);

        // 所有请求给这个拦截器处理
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        filterChainDefinitionMap.put("/**", "shiroLoginFilter");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }


    /**
     * 自定义的 subjectFactory
     * 禁用了 Session
     * @return
     */
    @Bean
    public DefaultWebSubjectFactory subjectFactory(){
        ASubjectFactory mySubjectFactory = new ASubjectFactory();
        return mySubjectFactory;
    }


    /**
     * session管理器
     * 禁用了 Session
     * sessionManager通过sessionValidationSchedulerEnabled禁用掉会话调度器，
     * @return
     */
    @Bean
    public DefaultSessionManager sessionManager(){
        DefaultSessionManager sessionManager = new DefaultSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(false);
        return sessionManager;
    }

    /**
     * 配置自定义的 Realm
     * @return
     */
    @Bean
    public Collection<Realm> getRealms(){
        Collection<Realm> realms = new ArrayList<>();

        // 配置自定义 UserRealm
        // 由于UserRealm里使用了自动注入，所以这里需要注入Realm而不是new新建
        userRealm.setAuthenticationTokenClass(UserAuthenticationToken.class);
        userRealm.setCredentialsMatcher(new PasswordCredentialsMatcher());//使用自定义的密码匹配器

        realms.add(userRealm);
        return realms;
    }
}
